﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace WorldOnFire
{
    public class CWaveManager
    {
        public enum eDifficulty
        {
            PUSSIES,
            FRIENDLY,
            NOT_SO_FRIENDLY,
            ANGRY,
            PENIS_GOES_WHERE,
            COUNT
        };

        public enum eSwarmType
        {
            BOMBER_DETACHMENT,
            DETONATOR_RUSH,
            SHORT_RANGE,
            LONG_RANGE,
            BALANCED,
            BOSS,
            //TEST,
            COUNT
        };

        public enum eReleaseRate
        {
            RON,
            SLOW,
            MEDIUM,
            FAST,
            RED_BALLS,
            COUNT
        };

        private class CSwarm
        {
            public const int CLUSTER_COUNT = 5;

            public eSwarmType m_eType;
            public eDifficulty m_eDifficulty;
            public eReleaseRate m_eReleaseRate;
            public int m_nWarpGateIndex;
            public int m_nEnemiesRemaining;
            public Queue<int>[] m_EnemyIndexQueue;
            public int[] m_nEnemySpawnRate;
            //public List<int>[] m_EnemyIndexList;
            //public int[] m_nListIndices;

            public CSwarm()
            {
                m_EnemyIndexQueue = new Queue<int>[(int)ABCEnemy.eEnemyType.COUNT];
                m_nEnemySpawnRate = new int[(int)ABCEnemy.eEnemyType.COUNT];

                for (int iEnemyType = 0; iEnemyType < (int)ABCEnemy.eEnemyType.COUNT; ++iEnemyType)
                {
                    m_EnemyIndexQueue[iEnemyType] = new Queue<int>();
                }
            }
        }

        private CObjectManager m_ObjectManager;
        private CRandom m_Ron;

        private List<ABCEnemy> m_EnemyList;
        private CWarpGate m_WarpGate;

        private Queue<List<CSwarm>> m_WaveQueue;

        public void Initialize(CObjectManager _ObjectManager, List<ABCEnemy> _EnemyList, CWarpGate _WarpGate)
        {
            m_ObjectManager = _ObjectManager;
            m_Ron = new CRandom();

            m_EnemyList = _EnemyList;
            m_WarpGate = _WarpGate;

            m_WaveQueue = new Queue<List<CSwarm>>();
        }

        public void EnqueueWave(int _nSwarmCount, eSwarmType[] _eTypes, eDifficulty[] _eDifficulties,
            eReleaseRate[] _eReleaseRates)
        {
            // Create the new wave
            List<CSwarm> Wave = new List<CSwarm>();

            // Loop through each swarm
            for (int iSwarm = 0; iSwarm < _nSwarmCount; ++iSwarm)
            {
                // Create a new swarm
                CSwarm NewSwarm = new CSwarm();
                NewSwarm.m_eType = _eTypes[iSwarm];
                NewSwarm.m_eDifficulty = _eDifficulties[iSwarm];
                NewSwarm.m_eReleaseRate = _eReleaseRates[iSwarm];

                // Add the swarm to the wave
                Wave.Add(NewSwarm);
            }

            // Add the wave to the queue
            m_WaveQueue.Enqueue(Wave);

            // If this is the first wave
            if(m_WaveQueue.Count == 1)
            {
                // Initialize the wave
                this.InitializeWave();
            }
        }

        private void InitializeWave()
        {
            List<CSwarm> Wave = m_WaveQueue.Peek();

            for (int iSwarm = 0; iSwarm < Wave.Count; ++iSwarm)
            {
                Wave[iSwarm].m_nWarpGateIndex = m_WarpGate.CreateWarpGate(10.0f, Wave[iSwarm].m_eReleaseRate);
                m_WarpGate.ScheduleForActivation(Wave[iSwarm].m_nWarpGateIndex);
                this.InitializeEnemyIndices(Wave[iSwarm]);
            }
        }

        private void InitializeEnemyIndices(CSwarm _Swarm)
        {
            int nDifficulty = (int)_Swarm.m_eDifficulty;
            int[] nEnemyCount = new int[(int)ABCEnemy.eEnemyType.COUNT];

            switch ((int)_Swarm.m_eType)
            {
                case (int)eSwarmType.BOMBER_DETACHMENT:
                    {
                        nEnemyCount[(int)ABCEnemy.eEnemyType.BOMBER] = 10 + (int)(7.5f * nDifficulty);
                        nEnemyCount[(int)ABCEnemy.eEnemyType.FRIGATE] = -5 + 3 * nDifficulty;
                        nEnemyCount[(int)ABCEnemy.eEnemyType.CARRIER] = (int)(0.5f * nDifficulty);
                    }
                    break;
                case (int)eSwarmType.DETONATOR_RUSH:
                    {
                        nEnemyCount[(int)ABCEnemy.eEnemyType.BOMBER] = 5 + 2 * nDifficulty;
                        nEnemyCount[(int)ABCEnemy.eEnemyType.DETONATOR] = 4 + 3 * nDifficulty;
                        nEnemyCount[(int)ABCEnemy.eEnemyType.FRIGATE] = 0 + 1 * nDifficulty;
                        nEnemyCount[(int)ABCEnemy.eEnemyType.GUARDIAN] = 1 + 1 * nDifficulty;
                        nEnemyCount[(int)ABCEnemy.eEnemyType.DESTROYER] = -1 + (int)(0.5f * nDifficulty);
                    }
                    break;
                case (int)eSwarmType.SHORT_RANGE:
                    {
                        nEnemyCount[(int)ABCEnemy.eEnemyType.BOMBER] = 5 + 3 * nDifficulty;
                        nEnemyCount[(int)ABCEnemy.eEnemyType.DETONATOR] = 2 + 1 * nDifficulty;
                        nEnemyCount[(int)ABCEnemy.eEnemyType.FRIGATE] = 3 + 3 * nDifficulty;
                        nEnemyCount[(int)ABCEnemy.eEnemyType.GUARDIAN] = 1 + 1 * nDifficulty;
                        nEnemyCount[(int)ABCEnemy.eEnemyType.DESTROYER] = 0 + nDifficulty;
                        nEnemyCount[(int)ABCEnemy.eEnemyType.BATTLESHIP] = -2 + nDifficulty;
                    }
                    break;
                case (int)eSwarmType.LONG_RANGE:
                    {
                        nEnemyCount[(int)ABCEnemy.eEnemyType.BOMBER] = 5 + 3 * nDifficulty;
                        nEnemyCount[(int)ABCEnemy.eEnemyType.DETONATOR] = 0 + 1 * nDifficulty;
                        nEnemyCount[(int)ABCEnemy.eEnemyType.FRIGATE] = 3 + 3 * nDifficulty;
                        nEnemyCount[(int)ABCEnemy.eEnemyType.GUARDIAN] = 1 + 1 * nDifficulty;
                        nEnemyCount[(int)ABCEnemy.eEnemyType.CRUISER] = 0 + nDifficulty;
                        nEnemyCount[(int)ABCEnemy.eEnemyType.BATTLESHIP] = -2 + nDifficulty;
                        nEnemyCount[(int)ABCEnemy.eEnemyType.CARRIER] = -1 + nDifficulty;
                    }
                    break;
                case (int)eSwarmType.BALANCED:
                    {
                        nEnemyCount[(int)ABCEnemy.eEnemyType.BOMBER] = 5 + 3 * nDifficulty;
                        nEnemyCount[(int)ABCEnemy.eEnemyType.DETONATOR] = 0 + 1 * nDifficulty;
                        nEnemyCount[(int)ABCEnemy.eEnemyType.FRIGATE] = 3 + 3 * nDifficulty;
                        nEnemyCount[(int)ABCEnemy.eEnemyType.GUARDIAN] = 1 + 1 * nDifficulty;
                        nEnemyCount[(int)ABCEnemy.eEnemyType.DESTROYER] = (int)(0.5f * + nDifficulty);
                        nEnemyCount[(int)ABCEnemy.eEnemyType.CRUISER] = -2 + nDifficulty;
                        nEnemyCount[(int)ABCEnemy.eEnemyType.BATTLESHIP] = -2 + nDifficulty;
                        nEnemyCount[(int)ABCEnemy.eEnemyType.CARRIER] = -2 + nDifficulty;
                    }
                    break;
                case (int)eSwarmType.BOSS:
                    {
                        nEnemyCount[(int)ABCEnemy.eEnemyType.FRIGATE] = 3 + 3 * nDifficulty;
                        nEnemyCount[(int)ABCEnemy.eEnemyType.GUARDIAN] = 1 + 1 * nDifficulty;
                        nEnemyCount[(int)ABCEnemy.eEnemyType.DESTROYER] = 1 + (int)(0.5f * +nDifficulty);
                        nEnemyCount[(int)ABCEnemy.eEnemyType.CRUISER] = -1 + nDifficulty;
                        nEnemyCount[(int)ABCEnemy.eEnemyType.BATTLESHIP] = -2 + nDifficulty;
                        nEnemyCount[(int)ABCEnemy.eEnemyType.JUGGERNAUT] = -3 + nDifficulty;
                    }
                    break;
                //case (int)eSwarmType.TEST:
                //    {
                //        nEnemyCount[(int)ABCEnemy.eEnemyType.JUGGERNAUT] = 1;
                //    }
                //    break;
            }

            _Swarm.m_nEnemiesRemaining = 0;

            for (int iEnemyType = 0; iEnemyType < (int)ABCEnemy.eEnemyType.COUNT; ++iEnemyType)
            {
                if (nEnemyCount[iEnemyType] < 0)
                {
                    nEnemyCount[iEnemyType] = 0;
                }

                // Increment the number of enemies remaining
                _Swarm.m_nEnemiesRemaining += nEnemyCount[iEnemyType];

                // Set the spawn rate for the enemy type
                _Swarm.m_nEnemySpawnRate[iEnemyType] =
                    1 + nEnemyCount[iEnemyType] / CSwarm.CLUSTER_COUNT;

                // Create each enemy
                ABCEnemy Enemy = m_ObjectManager.GetEnemy((ABCEnemy.eEnemyType)iEnemyType);
                for (int iEnemy = 0; iEnemy < nEnemyCount[iEnemyType]; ++iEnemy)
                {
                    // Create an enemy
                    int nEnemyIndex = Enemy.CreateEnemy(m_WarpGate, _Swarm.m_nWarpGateIndex);

                    // If an enemy was created
                    if (nEnemyIndex != -1)
                    {
                        // Enqueue it in the swarm's enemy index list
                        _Swarm.m_EnemyIndexQueue[iEnemyType].Enqueue(nEnemyIndex);
                    }
                    // If an enemy could not be created
                    else
                    {
                        // Decrement the number of enemies in the swarm
                        --_Swarm.m_nEnemiesRemaining;
                    }
                }
            }
        }

        public void Update(float _fDeltaTime)
        {
            // If there are no waves
            if (m_WaveQueue.Count == 0)
            {
                int nSwarmCount = 5;//m_Ron.Next(1, 6);
                CWaveManager.eSwarmType[] eTypes = new CWaveManager.eSwarmType[]
                {
                    (CWaveManager.eSwarmType)m_Ron.Next(0, (int)CWaveManager.eSwarmType.COUNT),
                    (CWaveManager.eSwarmType)m_Ron.Next(0, (int)CWaveManager.eSwarmType.COUNT),
                    (CWaveManager.eSwarmType)m_Ron.Next(0, (int)CWaveManager.eSwarmType.COUNT),
                    (CWaveManager.eSwarmType)m_Ron.Next(0, (int)CWaveManager.eSwarmType.COUNT),
                    (CWaveManager.eSwarmType)m_Ron.Next(0, (int)CWaveManager.eSwarmType.COUNT)
                };
                CWaveManager.eDifficulty[] eDifficulties = new CWaveManager.eDifficulty[]
                {
                    eDifficulty.PENIS_GOES_WHERE,
                    eDifficulty.PENIS_GOES_WHERE,
                    eDifficulty.PENIS_GOES_WHERE,
                    eDifficulty.PENIS_GOES_WHERE,
                    eDifficulty.PENIS_GOES_WHERE
                    //(CWaveManager.eDifficulty)m_Ron.Next(0, (int)CWaveManager.eDifficulty.COUNT),
                    //(CWaveManager.eDifficulty)m_Ron.Next(0, (int)CWaveManager.eDifficulty.COUNT),
                    //(CWaveManager.eDifficulty)m_Ron.Next(0, (int)CWaveManager.eDifficulty.COUNT),
                    //(CWaveManager.eDifficulty)m_Ron.Next(0, (int)CWaveManager.eDifficulty.COUNT),
                    //(CWaveManager.eDifficulty)m_Ron.Next(0, (int)CWaveManager.eDifficulty.COUNT)
                };
                CWaveManager.eReleaseRate[] eReleaseRates = new CWaveManager.eReleaseRate[]
                {
                    (CWaveManager.eReleaseRate)m_Ron.Next(0, (int)CWaveManager.eDifficulty.COUNT),
                    (CWaveManager.eReleaseRate)m_Ron.Next(0, (int)CWaveManager.eDifficulty.COUNT),
                    (CWaveManager.eReleaseRate)m_Ron.Next(0, (int)CWaveManager.eDifficulty.COUNT),
                    (CWaveManager.eReleaseRate)m_Ron.Next(0, (int)CWaveManager.eDifficulty.COUNT),
                    (CWaveManager.eReleaseRate)m_Ron.Next(0, (int)CWaveManager.eDifficulty.COUNT)
                };

                this.EnqueueWave(nSwarmCount, eTypes, eDifficulties, eReleaseRates);

                // Eject
                //return;
            }

            // Store the current wave
            List<CSwarm> CurrentWave = m_WaveQueue.Peek();

            // Loop through each swarm in the current wave
            bool bEnemiesRemaining = false;
            for (int iSwarm = 0; iSwarm < CurrentWave.Count; ++iSwarm)
            {
                // Store the swarm
                CSwarm Swarm = CurrentWave[iSwarm];

                // If there are still enemies
                if (Swarm.m_nEnemiesRemaining > 0)
                {
                    // The wave is not over
                    bEnemiesRemaining = true;
                }
                // If no enemies remain from this swarm
                else
                {
                    // Skip the spawning
                    continue;
                }

                // If the warp gate is ready to spawn MOAR enemies
                if (m_WarpGate.IsWarpReady(Swarm.m_nWarpGateIndex) == true)
                {
                    // Reset the warp timer
                    m_WarpGate.ResetWarpTimer(Swarm.m_nWarpGateIndex);

                    // Spawn MOAR enemies
                    for (int iEnemyType = 0; iEnemyType < (int)ABCEnemy.eEnemyType.COUNT; ++iEnemyType)
                    {
                        // Vary the number of enemies that spawn
                        int nDoingItRon = m_Ron.Next(-1, 2);
                        for (int iEnemy = 0; iEnemy < Swarm.m_nEnemySpawnRate[iEnemyType] + nDoingItRon
                            && Swarm.m_EnemyIndexQueue[iEnemyType].Count > 0; ++iEnemy)
                        {
                            // Schedule the enemy for activation
                            m_EnemyList[iEnemyType].ScheduleForActivation(Swarm.m_EnemyIndexQueue[iEnemyType].Dequeue());
                            --Swarm.m_nEnemiesRemaining;
                        }
                    }
                }
            }

            // If all enemies have been killed
            if (bEnemiesRemaining == false)
            {
                // Loop through each enemy type to determine if any enemies remain
                for (int iEnemyType = 0; iEnemyType < m_EnemyList.Count; ++iEnemyType)
                {
                    if (m_EnemyList[iEnemyType].GetActiveObjectCount() != 0)
                    {
                        bEnemiesRemaining = true;
                    }
                }

                if (bEnemiesRemaining == false)
                {
                    // Go to the next wave
                    List<CSwarm> Wave = m_WaveQueue.Dequeue();
                    for (int iSwarm = 0; iSwarm < Wave.Count; ++iSwarm)
                    {
                        m_WarpGate.ScheduleForDeactivation(Wave[iSwarm].m_nWarpGateIndex);
                    }

                    if (m_WaveQueue.Count > 0)
                    {
                        this.InitializeWave();
                    }
                }
            }
        }
    }
}
